import {bMount, mounted} from "@/lifecycle/types";
import {strToElement, vNodeToElement} from "@/parse";
import {eleToVNode} from "@/complie/element";
import {AppComponent, $el, $children, $options, _uid, _vNode, SybComponentName, $parent, _isMounted} from "@/instance";
import {isObject} from "@/utils";
import {_nextTickCbs} from "@/reactive";

const _mount = {
  // beforeMount: check with component , if true, component will ready to mount.
  [bMount](vm) {
    const {el, template} = vm[$options];
    if (el || template) {
      checkComponent(vm);
      // template will replace the el
      vm[$el] = template ? initTemplate(template) : initEl(el);
      // continue to compile to get `vNode` with mine commands.
      vm[_uid] === 1 && eleToVNode(vm);
    }
    // console.log('mine beforeMount')
  },
  //   mounted：Vue instance replace the dom.
  [mounted](vm) {
    // console.log('mine mounted');
    // push the `vNode` to Element tag.
    // this callback to user to use `$nextTick`
    const cb = _nextTickCbs[0];
    typeof cb === 'function' && cb()
    _nextTickCbs.splice(0, 1);
    // console.log(_nextTickCbs,'_nextTickCbs')
    if (vm[SybComponentName] === AppComponent) {
      const ele = vNodeToElement(vm[_vNode]);
      const fragmentChildren = Array.from(ele.childNodes);
      while (vm[$el].firstChild) {
        vm[$el].firstChild.remove();
      }

      fragmentChildren.forEach(childElement => {
        vm[$el].appendChild(childElement);
      });

      console.log(ele, 'dom更新完毕', vm[$el])
    }
  }
}

export default _mount;

export const initEl = (el) => {
  if (el) {
    if (el instanceof HTMLElement) return el;
    if (typeof el === 'string' && document.querySelector(el)) {
      return document.querySelector(el)
    }
  }
}

export const initTemplate = (template) => {
  // 不规范的字符串等错误直接暴露给用户
  return strToElement(template);
}

//   判断如果有子组件，给 `$children` 进行push动作，并先执行子组件的 `created` , `mounted` 钩子
export const checkComponent = (vm) => {
  const {components} = vm[$options];
  if (isObject(components)) {
    // ... 判断子组件 继承自 Vue 才放行
    for (let comName in components) {
      const comFn = components[comName];
      if (typeof comFn !== "function") throw new Error('f');
      const comInstance = new comFn();
      if (!(comInstance instanceof Vue)) {
        throw new Error('options.components中' + comName + '不是一个Vue组件');
      }
      comInstance[$parent] = vm;
      comInstance[SybComponentName] = comName; // 当前组件的名字
      eleToVNode(comInstance);
      vm[$children].push(comInstance);
    }
  } else {
    if (components !== undefined) {
      throw new Error('options.components 必须是一个对象');
    }
  }
}
